// tests.cpp : Defines the entry point for the console application.
//

#include "consistenz.h"

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include "dictionary.h"
#include "variant.h"
#include <iostream>
#include "objectimpl.h"
#include "dbdictionary.h"
#include "core.h"

using namespace std;
using namespace CnsZ;

void tests();
void testMemoryStreams();
void testConcatStaticString();
void testDictionary();
void testDDictionary();
void testArrays();

  class MySettings : public Settings {
  public:
    Int64 getInteger(IntegerSettingId id) {
      Int64 res = Settings::getInteger(id);
      return res;
    }

    Real getReal(RealSettingId id) {
      Real res = Settings::getReal(id);
      return res;
    }

    String getString(StringSettingId id) {
      switch(id) {
      case AppName:
        return String(L"Consistenz Test Application");
      case AppStorageFileName:
        return String(L"test.consistenz.storage");
      }
      String res = Settings::getString(id);
      return res;
    }

    P<Object> getObject(ObjectSettingId id) {
      P<Object> res = Settings::getObject(id);
      return res;
    }
  };

  class MySystem : public System {
  public:
    String findFile(const String& name) {
      String res = System::findFile(name);
      return res;
    }

    P<Stream> file(const String& pathName, int options = CreateIfNotFound) {
      P<Stream> res = System::file(pathName, options);
      return res;
    }
    /*
    P<Module> loadModule(const String& name) {
      P<Module> res = System::loadModule(name);
      return res;
    }
    */
  };

int main(int /*argc*/, char* /*argv*/[]) {
  MySystem mySystem;
  MySettings mySettings;

  try {
    Core::initialize(mySystem, mySettings);

    tests();
  }
  catch(...) {
  }

  if(_CrtDumpMemoryLeaks()) {
    wcout << std::endl << "Detected memory leaks!" << std::endl;
  }
  return 0;
}

void tests() {
  try {

    wcout << std::endl << "------------ testArrays() ----------" << std::endl << std::endl;
    testArrays();

    wcout << std::endl << "-------- testMemoryStreams() -------" << std::endl << std::endl;
    testMemoryStreams();

    wcout << std::endl << "----- testConcatStaticString() -----" << std::endl << std::endl;
    testConcatStaticString();

    wcout << std::endl << "--------- testDictionary() ---------" << std::endl;
    testDictionary();

    wcout << std::endl << "-------- testDDictionary() --------" << std::endl;
    testDDictionary();

    wcout << std::endl << "------------------------------------" << std::endl << std::endl;
    wcout << "Tests are finished" << std::endl;
  }
  catch(const Exception& e) {
    wcout << std::endl << "--- !!! Unexpected exception !!! ---" << std::endl << std::endl
         << "  Exception::Message: \"";
    wcout << e.message() << "\"" << std::endl;
  }
  catch(...) {
    wcout << std::endl << "--- !!! Unexpected exception !!! ---" << std::endl
         << "  catch(...)" << std::endl;
  }
}

void testConcatStaticString() {
  String concatstr(L"concatenate ");
  wcout << "Parameter 1:(" << concatstr.length() << ")\"" << concatstr << '"' << std::endl;
  CNSZ_DEFINE_STATIC_STRING(sItem1, L"sItem1 +");
  wcout << "Parameter 2: (" << CNSZ_GET_STATIC_STRING(sItem1).length() << ")\"" << CNSZ_GET_STATIC_STRING(sItem1) << '"' << std::endl;
  CNSZ_DEFINE_STATIC_STRING(sItem2, L" sItem2");
  wcout << "Parameter 3: (" << CNSZ_GET_STATIC_STRING(sItem2).length() << ")\"" << CNSZ_GET_STATIC_STRING(sItem2) << '"' << std::endl;
  concatstr = String(L"concatenate ") + CNSZ_GET_STATIC_STRING(sItem1) + CNSZ_GET_STATIC_STRING(sItem2);
  wcout << "   Result  :(" << concatstr.length() << ")\"" << concatstr << '"' << std::endl;
}

#include "valueint.h"

void dumpCurPos(const ObjectIterator* iter) {
  if(iter->done()) {
    wcout << "  done." <<  std::endl;
  }
  else {
    wcout << "  next: " << iter->identifer() << ':' << iter->object() <<  std::endl;
  }
}

void dumpIterator(ObjectIterator* iter) {
  wcout << "  count: " << iter->count() << std::endl;
  while(!iter->done()) {
    dumpCurPos(iter);
    iter->next();
  }
}

void testDictionary() {
  wcout << std::endl << "test Dictionary::New(): " << std::endl;
  P<Dictionary> root = Dictionary::New();

  wcout << "  " << root << "; isA.toString:'" << root->isA()->name() << '\'' << std::endl;

  wcout << std::endl << "test Dictionary::putAt(): " << std::endl;
  wcout << "  " << root << std::endl;

  root->putAt(0, Dictionary::New().get());
  wcout << "  " << root << std::endl;

  P<Dictionary> d = castTo<Dictionary>(root->getAt(0).get());
  d->putAt(ValueString::New(L"Name"), ValueString::New(L"Dmitry"));
  d->putAt(ValueString::New(L"Surname"), ValueString::New(L"Novikov"));
  d->putAt(ValueString::New(L"Fathername"), ValueString::New(L"Andreevich"));
  d->putAt(ValueString::New(L"Mother's maden name"), ValueString::New(L"IDontTell"));

  root->putAt(ValueInt::New(1), Dictionary::New().get());
  wcout << "  " << root << std::endl;

  wcout << std::endl << "test Dictionary::putAt()'s out parameter: " <<  std::endl;
  root->putAt(ValueInt::New(2), ValueString::New(L"<OldValue>").get());
  wcout << "  " << root << std::endl;

  P<Object> replaced;
  root->putAt(ValueInt::New(2), ValueString::New(L"<NewValue>").get(), &replaced);
  wcout << "  replaced " << replaced->toString() << " at key 2" << std::endl;
  wcout << "  " << root << std::endl;
  
  root->putAt(ValueString::New(L"3"), Dictionary::New().get(), &replaced);
  wcout << "  " << root << std::endl;
  wcout << "  " << (replaced.get()==0 ? "replaced==0 -- ok" : "replaced!=0") << std::endl;

  wcout << std::endl << "test Dictionary::Iterator:" << std::endl;
  P<ObjectIterator> iter = root->newIterator();
  wcout << "  d->newIterator(0,0):" << std::endl;
  dumpIterator(iter);
  iter = d->newIterator(ValueString::New(L"A"), ValueString::New(L"O"));
  wcout << std::endl << "  d->newIterator(ValueString::New(L\"A\"), ValueString::New(L\"M\")):" << std::endl;
  dumpIterator(iter);
  iter = d->newIterator(ValueString::New(L"M"), ValueString::New(L"T"));
  wcout << std::endl << "  d->newIterator(ValueString::New(L\"M\"), ValueString::New(L\"T\")):" << std::endl;
  dumpIterator(iter);
  iter = d->newIterator(ValueString::New(L"T"), ValueString::New(L"M"));
  wcout << std::endl << "  d->newIterator(ValueString::New(L\"T\"), ValueString::New(L\"M\")):" << std::endl;
  dumpIterator(iter);

  wcout << std::endl << "test Dictionary::Iterator::rewind" << std::endl;
  iter->seekStart();
  dumpCurPos(iter);

  wcout << std::endl << "test NullFactory exception:" << std::endl;
  try {
    Object::classInfo()->createInstance();
    wcout << "fail" << std::endl;
  }
  catch(const NullFactory& e) {
    wcout << "  Caught. Message: " << e.message() << ".NullFactory" << std::endl;
  }
}

void testDDictionary() {
  wcout << std::endl << "test Handle::New():" << std::endl;
  P<Domain> dom = Domain::New();
  wcout << dom << std::endl;

  wcout << std::endl << "test DDictionary::New():" << std::endl;
  P<DDictionary> root = DDictionary::New();
  dom->putAt(ValueString::New(L"Root"), root);
  P<Handle> hRoot = root->handle();

  wcout << "  " << root << "; isA.toString:'" << root->isA()->name() << '\'' << std::endl;

  wcout << std::endl << "test DDictionary::putAt():" << std::endl;
  wcout << "  " << root << std::endl;

  P<DDictionary> d = DDictionary::New();
  d->putAt(ValueString::New(L"Name"), ValueString::New(L"Dmitry"));
  d->putAt(ValueString::New(L"Surname"), ValueString::New(L"Novikov"));

  root->putAt(ValueString::New(L"Test"), d);
  root->putAt(0, d->handle());

  wcout << "  " << root << std::endl;

}

#include "valuearray.h"


void testArrays() {
  A<UInt8> arr;
  arr.reserve(10);
  arr.resize(10,0);
  wcout << "Array class: " << arr.valueArray()->isA()->name() << std::endl;
  wcout << "  " << arr << std::endl;
  arr.resize(15, 1);
  wcout << "  " << arr << std::endl;
  arr.resize(5);
  wcout << "  " << arr << std::endl;
  UInt8 ini[] = {1,2,3,4,5,6,7,8,9};
  arr.insert(arr.begin()+2, ini, ini+9);
  wcout << "  " << arr << std::endl;
  arr = A<UInt8>(5);
  A<UInt8> arr2 = arr;
  arr2.insert(arr2.begin()+2, ini, ini+9);
  wcout << "  " << arr2 << std::endl;
  arr2.erase(arr2.begin()+5, arr2.begin()+10);
  wcout << "  " << arr2 << std::endl;
  arr2.push_back(10);
  wcout << "  " << arr2 << std::endl;
  wcout << "Testing bounds checking:" << std::endl;
  try {
	  wcout << arr[20] << std::endl;
  }
  catch(const Exception& e) {
	  wcout << "  Exception: " << e.message() << std::endl;
  }
  try {
	  wcout << arr.insert(arr.end()+1, 8) << std::endl;
  }
  catch(const Exception& e) {
	  wcout << "  Exception: " << e.message() << std::endl;
  }
}

#include "filer.h"
#include <memory>

void testMemoryStreams() {
  using namespace CnsZ;
  std::auto_ptr<Byte> buffer(new Byte[128]);
  P<Stream> flatmem = Stream::NewFlatMemory(buffer.get(), 128);
  P<Filer> filer = CopyFiler::New(flatmem);

  P<DDictionary> dict1 = DDictionary::New();
  P<Handle> h = Handle::New();
  dict1->putAt(ValueAString::New("Name"), ValueAString::New("RuSlan"));
  dict1->putAt(ValueString::New(L"Handle"), h);
  dict1->putAt(h, DDictionary::New());

  wcout << "Initialized test DDictionary: "
    << std::endl << dict1 << std::endl;
  dict1->write(*filer);
  size_t dataSize = flatmem->tell();
  flatmem->seek(0);

  P<Stream> memPages = Stream::NewMemoryPages(10);
  memPages->wrBytes(dataSize, flatmem);
  memPages->seek(0);
  
  filer = CopyFiler::New(memPages);

  P<DDictionary> dict2 = DDictionary::New();
  dict2->read(*filer);
  wcout << "Loaded test DDictionary: "
    << std::endl << dict2 << std::endl;
  wcout << "Length: " << filer->stream().tell() << std::endl;

}